#ifndef EXCHANGE_H
#define EXCHANGE_H
#include "../common/message.pb.h"
#include "../common/Helper.hpp"
#include "../common/Log.hpp"
#include "../common/project.pb.h"
#include <google/protobuf/map.h>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <unordered_map>
#include <mutex>
using namespace std;

// 1. 定义交换机类
struct Exchange 
{
    Exchange() {}
    using ptr = shared_ptr<Exchange>;
    Exchange(const string& ename, 
        MessageQueue::ExchangeType etype, 
        bool edurable, 
        bool eauto_delete, 
        google::protobuf::Map<string, string> eargs) :
    name(ename), type(etype), durable(edurable), auto_delete(eauto_delete), args(eargs)
    {}

    // args存储键值对，在进行数据库存储的时候，组织字符串格式进行存储
    // 内部解析这种key=val&key=val，把数据存储到数据库中
    void SetArgs(const string& str_args)
    {
        // key=val&key=val
        vector<string> vec_args;
        size_t ret = StrHelper::split(str_args, "&", vec_args);
        for(auto& str : vec_args)
        {
            // 提取出来key value
            size_t pos = str.find('=');
            string key = str.substr(0, pos);
            string value = str.substr(pos + 1);
            args[key] = value;
        }
    }

    // 从args中把数据存储到字符串中
    string GetArgs()
    {
        string result = "";
        for(auto& kv : args)
            result += kv.first + "=" + kv.second + "&";
        return result;
    }
    // 名称，类型，持久化，自动删除，其他参数
    string name;
    MessageQueue::ExchangeType type;
    bool durable;
    bool auto_delete;
    google::protobuf::Map<string, string> args;
};

// 2. 定义交换机持久化管理类--数据存储在sqlite数据库中
class ExchangeMapper
{
    using ExchangeMap = unordered_map<string, Exchange::ptr>;
public:
    ExchangeMapper(const string& db_file) : _sql_helper(db_file)
    {
        string path = FileHelper::parentDirectory(db_file);
        FileHelper::createDirectory(path);
        assert(_sql_helper.open());
        CreateTable();
    }

    // 创建数据表
    void CreateTable()
    {
        const string& create_sql = "create table if not exists exchange_table \
        (name varchar(32) primary key, type int, durable int, auto_delete int, \
        args varchar(128))";
        bool ret = _sql_helper.exec(create_sql, nullptr, nullptr);
        if(ret == false)
        {
            lg(LOG_ERROR_CODE, "创建交换机数据表失败!");
            abort();
        }
    }

    // 移除数据表
    void RemoveTable()
    {
        const string& remove_sql = "drop table if exists exchange_table";
        bool ret = _sql_helper.exec(remove_sql, nullptr, nullptr);
        if(ret == false)
        {
            lg(LOG_ERROR_CODE, "删除交换机数据表失败!");
            abort();
        }
    }

    // 插入数据
    bool Insert(Exchange::ptr& exp)
    {
        stringstream ss;
        ss << "insert into exchange_table values(";
        ss << "'" << exp->name << "', ";
        ss << exp->type << ", ";
        ss << exp->durable << ", ";
        ss << exp->auto_delete << ", ";
        ss << "'" << exp->GetArgs() << "');";
        return _sql_helper.exec(ss.str(), nullptr, nullptr);
    }

    // 移除数据
    void Remove(const string& name)
    {
        stringstream ss;
        ss << "delete from exchange_table where name=";
        ss << "'" << name << "';";
        _sql_helper.exec(ss.str(), nullptr, nullptr);
    }

    // 读取表的数据恢复
    ExchangeMap Recovery()
    {
        unordered_map<string, Exchange::ptr> result;
        const string& covery_sql = "select name, type, durable, auto_delete, args from exchange_table;";
        _sql_helper.exec(covery_sql, selectCallback, &result);
        return result;
    }

private:
    static int selectCallback(void* arg, int numcol, char** row, char** fileds)
    {
        ExchangeMap* result = (ExchangeMap*)arg;
        auto exp = make_shared<Exchange>();
        exp->name = row[0];
        exp->type = MessageQueue::ExchangeType(stoi(row[1]));
        exp->durable = (bool)stoi(row[2]);
        exp->auto_delete = (bool)stoi(row[3]);
        if(row[4])
            exp->SetArgs(row[4]);
        result->insert(make_pair(exp->name, exp));
        return 0;
    }

private:
    SqliteHelper _sql_helper;
};


// 3. 定义交换机数据内存管理类
class ExchangeManager
{
public:
    using ptr = shared_ptr<ExchangeManager>;
    ExchangeManager(const string& db_file) : _mapper(db_file)
    {
        _exchange = _mapper.Recovery();
    }

    // 声明交换机
    bool DeclareExchange(const string& name,
        MessageQueue::ExchangeType type, 
        bool durable, bool auto_delete,
        const google::protobuf::Map<string, string>& args)
    {
        unique_lock<mutex> lock(_mutex);
        // 如果存在就不创建了
        auto it = _exchange.find(name);
        if(it != _exchange.end())
            return true;
        auto exp = make_shared<Exchange>(name, type, durable, auto_delete, args);
        if(durable)
        {
            bool ret = _mapper.Insert(exp);
            if(ret == false)
                return false;
        }
        _exchange.insert(make_pair(name, exp));
        return true;
    }

    // 删除交换机
    void DeleteExchange(const string& name)
    {
        unique_lock<mutex> lock(_mutex);
        // 不存在就直接返回
        auto it = _exchange.find(name);
        if(it == _exchange.end())
            return;
        if(it->second->durable == true)
            _mapper.Remove(name);
        _exchange.erase(name);
    }

    // 根据名称获取指定交换机对象
    Exchange::ptr selectExchange(const string& name)
    {
        unique_lock<mutex> lock(_mutex);
        auto it = _exchange.find(name);
        // 如果不存在就直接返回
        if(it == _exchange.end())
            return Exchange::ptr();
        return it->second;
    }

    // 判断是否存在
    bool exists(const string& name)
    {
        unique_lock<mutex> lock(_mutex);
        auto it = _exchange.find(name);
        // 如果不存在就直接返回
        if(it == _exchange.end())
            return false;
        return true;
    }
    // 清除
    void clear()
    {
        unique_lock<mutex> lock(_mutex);
        _mapper.RemoveTable();
        _exchange.clear();
    }

    size_t ExchangeSize()
    {
        unique_lock<mutex> lock(_mutex);
        return _exchange.size();
    }
private:
    mutex _mutex;
    ExchangeMapper _mapper;
    unordered_map<string, Exchange::ptr> _exchange;
};
#endif